#include <algorithm>
#include "dlna_manager.h"
#include "cast_engine_log.h"
#include "utils.h"
#include "connection_manager.h"
#include "dm_constants.h"
#include "json.hpp"
namespace OHOS {
namespace CastEngine {
namespace CastEngineService {
DEFINE_CAST_ENGINE_LABEL("Cast-Dlna-Manager");
using namespace OHOS::EventFwk;
using namespace OHOS::DistributedHardware;

DlnaManager &DlnaManager::GetInstance()
{
    static DlnaManager instance{};
    return instance;
}

DlnaManager::DlnaManager() {}
DlnaManager::~DlnaManager() {}

void DlnaManager::Init(std::shared_ptr<IDlnaManagerListener> listener)
{
    CLOGI("DlnaManager Init");
    if (!listener) {
        CLOGE("Listener is null");
        return;
    }
    std::lock_guard<std::mutex> lock(mutex_);
    if (listener_) {
        CLOGW("Already initialized");
        return;
    }
    listener_ = listener;
}

void DlnaManager::DeInit()
{
    CLOGI("DlnaManager DeInit");
    {
        std::lock_guard<std::mutex> lock(mutex_);
        if (!listener_) return;
    }
    if (castStateObserver_) {
        castStateObserver_->StopEventSubscriber();
        castStateObserver_.reset();
    }
    StopDiscovery();
    if (isStackInitialized_) {
        dmcApi_.DestroyEnv();
        isStackInitialized_ = false;
    }
    {
        std::lock_guard<std::mutex> lock(mutex_);
        listener_.reset();
    }
}

int DlnaManager::RegisterWifiStatusChangeListener()
{
    auto client = Wifi::WifiDevice::GetInstance(WIFI_DEVICE_ABILITY_ID);
    if (!client) {
        CLOGE("WifiDevice instance null");
        return -1;
    }
    sptr<Wifi::IWifiDeviceCallBack> cb = new WifiCallback();
    std::vector<std::string> events = {EVENT_STA_POWER_STATE_CHANGE, EVENT_STA_CONN_STATE_CHANGE};
    return client->RegisterCallBack(cb, events);
}

void DlnaManager::StartDiscovery()
{
    CLOGI("StartDiscovery");
    devReportEnable_.store(true);
    if (HasDevice() && listener_) {
        listener_->OnDeviceFound(devices_);
    }
    SendCastMessage(Message(MessageId::DLNA_MSG_STACK_INIT));
}

void DlnaManager::StartDlnaApiStackInit()
{
    CLOGI("StartDlnaApiStackInit using DMC API");
    DlnaDmc::DmcCbType cb{};
    cb.deviceUpCb = [this](const DlnaDmc::UploadDeviceInfo &info) {
        CLOGI("Dlna DeviceUpCb: deviceId=%{public}u, udn=%{public}s, friendlyName=%{public}s, baseURL=%{public}s",
            info.deviceId, info.udn.c_str(), info.friendlyName.c_str(), info.baseURL.c_str());

        CastInnerRemoteDevice dev;
        dev.deviceId           = info.udn;
        dev.deviceName         = info.friendlyName;
        dev.dlnaDeviceId       = info.deviceId;
        dev.modelName          = info.modelName;
        dev.manufacturerName   = info.manufacturer;
        dev.deviceType         = DeviceType::DEVICE_HW_TV;
        dev.capabilityInfo     = static_cast<uint32_t>(ProtocolType::DLNA);
        dev.capability         = CapabilityType::DLNA;
        dev.mediumTypes        = static_cast<uint32_t>(NotifyMediumType::COAP);

        // 后续：注册到管理器里
        AddDeviceMap(info.udn, info.deviceId);
        OnDeviceInfoFound(dev);
        return DlnaDmc::DMC_SUCCESS;
    };
    cb.deviceDownCb = [this](uint32_t id) {
        CLOGI("DMC_Callbac deviceDownCb, dlnaDevId=%{public}u, mappedUuid=%{public}s", id, GetDeviceUuid(id).c_str());
        OnDeviceOffline(id);
        return DlnaDmc::DMC_SUCCESS;
    };
    //std::string iface = GetIpAddr();
    auto ret = dmcApi_.InitEnv("wlan0", cb);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        CLOGE("DMC InitEnv failed: %d", ret);
        return;
    }
    isStackInitialized_ = true;
    // Subscribe screen lock events
    if (!castStateObserver_) {
        castStateObserver_ = std::make_shared<CastStateObserver>(this);
        castStateObserver_->StartEventSubscriber();
    }
    RegisterWifiStatusChangeListener();
    CLOGI("StartDlnaApiStackInit called, InitEnv returned %d", ret);
}

void DlnaManager::StopDiscovery()
{
    CLOGI("StopDiscovery");
    devReportEnable_.store(false);
    SendCastMessage(Message(MessageId::DLNA_MSG_STACK_DESTROY));
}

std::shared_ptr<IDlnaManagerListener> DlnaManager::GetListener()
{
    std::lock_guard<std::mutex> lock(mutex_);
    return listener_;
}

void DlnaManager::SetListener(std::shared_ptr<IDlnaManagerListener> listener)
{
    std::lock_guard<std::mutex> lock(mutex_);
    listener_ = listener;
}

bool DlnaManager::HasListener() const
{
    std::lock_guard<std::mutex> lock(mutex_);
    return listener_ != nullptr;
}

void DlnaManager::ResetListener()
{
    SetListener(nullptr);
}

void DlnaManager::OnDeviceInfoFound(const CastInnerRemoteDevice& newDevice)
{
    CLOGI("OnDeviceInfoFound: %{public}s; networkId :%{public}s", newDevice.deviceId.c_str(), newDevice.networkId.c_str());
    AddDevice(newDevice);
    DmDeviceInfo dmInfo{};
    if (strcpy_s(dmInfo.deviceId, sizeof(dmInfo.deviceId), newDevice.deviceId.c_str()) != 0) {
        CLOGE("Failed to copy deviceId into DmDeviceInfo");
    }
    json j;
    if (!newDevice.bleMac.empty()) {
        j[PARAM_KEY_BLE_MAC] = newDevice.bleMac;
    } else {
        j[PARAM_KEY_WIFI_IP] = newDevice.wifiIp;
    }
    dmInfo.extraData = j.dump();
    CastDeviceDataManager::GetInstance().AddDevice(newDevice, dmInfo);
    if (devReportEnable_.load() && HasListener()) {
        GetListener()->OnDeviceFound(GetDevices());
    }
}

void DlnaManager::OnDeviceOffline(unsigned long deviceId)
{
    std::string uuid = GetDeviceUuid(static_cast<uint32_t>(deviceId));
    CLOGI("Device offline: %s", uuid.c_str());
    CastDeviceDataManager::GetInstance().RemoveDlnaInfo(uuid);
    if (dlnaCallback_) {
        dlnaCallback_->OnDeviceOffline(deviceId);
    }
    RemoveDevice(uuid);
    if (listener_) {
        listener_->OnDeviceOffline(uuid);
    }
}

std::string DlnaManager::GetIpAddr()
{
    CLOGI("GetIpAddr in");
    return Utils::GetWifiIp();
}

bool DlnaManager::AddDeviceMap(const std::string &uuid, uint32_t id) {
    std::lock_guard<std::mutex> lock(mutex_);
    deviceHashMap_[uuid] = id;
    return true;
}

void DlnaManager::AddDevice(const CastInnerRemoteDevice& newDevice)
{
    std::lock_guard<std::mutex> lock(mutex_);
    devices_.emplace_back(newDevice);
    deviceHashMap_[newDevice.deviceId] = newDevice.dlnaDeviceId;
}

void DlnaManager::RemoveDevice(const std::string &dlnaDeviceId)
{
    std::lock_guard<std::mutex> lock(mutex_);
    devices_.erase(std::remove_if(devices_.begin(), devices_.end(),
        [&](auto &d){ return d.deviceId == dlnaDeviceId; }), devices_.end());
    deviceHashMap_.erase(dlnaDeviceId);
}

bool DlnaManager::HasDevice() const
{
    std::lock_guard<std::mutex> lock(mutex_);
    return !devices_.empty();
}

std::vector<CastInnerRemoteDevice> DlnaManager::GetDevices() const
{
    std::lock_guard<std::mutex> lock(mutex_);
    return devices_;
}

void DlnaManager::ClearDevice()
{
    CLOGI("deviceHashMap start clear");
    std::lock_guard<std::mutex> lock(mutex_);
    for (auto &d : devices_) {
        if (listener_) listener_->OnDeviceOffline(d.deviceId);
    }
    devices_.clear();
    deviceHashMap_.clear();
    CLOGI("DLNA all clean");
}

std::string DlnaManager::GetDeviceUuid(uint32_t dlnaDevId)
{
    CLOGI("GetDeviceUuid in, dlnaDevId is %d", dlnaDevId);
    if (dlnaDevId == 0) {
        CLOGE("dlnaDevId is invalid");
        return "";
    }
    std::lock_guard<std::mutex> lock(mutex_);
    for (auto &p : deviceHashMap_) {
        if (p.second == dlnaDevId) return p.first;
    }
    return {};
}
std::shared_ptr<DlnaCallback> DlnaManager::GetDlnaCallback()
{
    return dlnaCallback_;
}

void DlnaManager::SetDlnaCallback(std::shared_ptr<DlnaCallback> dlnaCallback)
{
    dlnaCallback_ = dlnaCallback;
}

void DlnaManager::SetPlaybackStatus(DlnaManager::DlnaPlaybackStatus playbackStatus)
{
    CLOGI("SetPlaybackStatus is %{public}d", playbackStatus);
    playbackStatus_ = playbackStatus;
}

DlnaManager::DlnaPlaybackStatus DlnaManager::GetPlaybackStatus() const
{
    CLOGI("GetPlaybackStatus is %{public}d", playbackStatus_);
    return playbackStatus_;
}

void CastEngineService::WifiCallback::OnWifiStateChanged(int state)
{
    CLOGI("OnWifiStateChanged state = %d", state);
}

void CastEngineService::WifiCallback::OnWifiConnectionChanged(int state, const OHOS::Wifi::WifiLinkedInfo &info)
{
    CLOGI("OnWifiConnectionChanged enter state:%{public}d ", state);
    if (state == Wifi::ConnState::CONNECTED) {
        DlnaManager::GetInstance().SendCastMessage(Message(DlnaManager::MessageId::DLNA_MSG_WIFI_CONNECTED));
    } else if (state == Wifi::ConnState::DISCONNECTED) {
        DlnaManager::GetInstance().SendCastMessage(Message(DlnaManager::MessageId::DLNA_MSG_WIFI_DISCONNECTED));
    }
}

void CastEngineService::WifiCallback::OnWifiRssiChanged(int rssi)
{
    CLOGI("OnWifiRssiChanged success");
}

void CastEngineService::WifiCallback::OnWifiWpsStateChanged(int state, const std::string &pinCode)
{
    CLOGI("OnWifiWpsStateChanged success");
}

void CastEngineService::WifiCallback::OnStreamChanged(int direction)
{
    CLOGI("OnStreamChanged success");
}

void WifiCallback::OnDeviceConfigChanged(OHOS::Wifi::ConfigChange value)
{
    CLOGI("OnDeviceConfigChanged success");
}

sptr<IRemoteObject> WifiCallback::AsObject()
{
    return nullptr;
}

CastStateObserver::CastStateObserver(DlnaManager* connection)
    : connection_(connection)
{}

CastStateObserver::~CastStateObserver() = default;

CastStateEventSubscriber::CastStateEventSubscriber(const CommonEventSubscribeInfo &info, DlnaManager* connection)
    : CommonEventSubscriber(info), connection_(connection)
{}

void CastStateObserver::StartEventSubscriber()
{
    CLOGI("StartEventSubscriber");
    MatchingSkills skills;
    skills.AddEvent(CommonEventSupport::COMMON_EVENT_SCREEN_LOCKED);
    CommonEventSubscribeInfo info(skills);
    info.SetThreadMode(CommonEventSubscribeInfo::COMMON);
    castSubscriber_ = std::make_shared<CastStateEventSubscriber>(info, connection_);
    bool subscribeResult = CommonEventManager::SubscribeCommonEvent(castSubscriber_);
    CLOGI("Subscribe screen-lock common event result=%d", subscribeResult);
}

void CastStateObserver::StopEventSubscriber()
{
    if (castSubscriber_) {
        CommonEventManager::UnSubscribeCommonEvent(castSubscriber_);
        castSubscriber_.reset();
    }
    CLOGI("StopEventSubscriber done");
}

void CastStateEventSubscriber::OnReceiveEvent(const OHOS::EventFwk::CommonEventData &data)
{
    CLOGI("OnReceiveEvent in");
    std::string action = data.GetWant().GetAction();
    CLOGI("CastStateEventSubscriber::OnReceiveEvent: action = %{public}s", action.c_str());
    if (action != EventFwk::CommonEventSupport::COMMON_EVENT_SCREEN_LOCKED) {
        return;
    }
    /* 当播放断开连接时, 且为锁屏状态下，调用Deinit */
    if (connection_->GetPlaybackStatus() == DlnaManager::STOPPED) {
        CLOGI("Current playback stopped, Start to destroy the DLNA protocol stack.");
        connection_->HandleMessage(Message(DlnaManager::MessageId::DLNA_MSG_STACK_DESTROY));
    }else {
        CLOGI("Currently playing, not disconnected");
    }
}

void DlnaManager::HandleMessage(const Message &msg)
{
    switch (static_cast<MessageId>(msg.what_)) {
        /* 处理启动协议栈会话 */
        case MessageId::DLNA_MSG_STACK_INIT:
            CLOGW("Handler Start execution DlnaApiStackInit");
            /* 判断为false 启动协议栈, 否则表示协议栈已经启动 */
            if (!isStackInitialized_) {
                StartDlnaApiStackInit();
            }
            break;
        /* 处理停止协议栈会话 */
        case MessageId::DLNA_MSG_STACK_DESTROY:
            CLOGW("Handler Start execution DlnaApiStackDestroy");
            /* 判断为true 调用StopDiscovery 将值置为false 再销毁协议栈 */
            if (isStackInitialized_) {
                dmcApi_.DestroyEnv();
                isStackInitialized_ = false;
            }
            break;
        case MessageId::DLNA_MSG_WIFI_CONNECTED:
            CLOGW("Handler WiFi had connected");
            /* 只有当上层调用发现设备并且协议栈没有启动时执行 */
            if (devReportEnable_.load() && !isStackInitialized_) {
                StartDiscovery();
            }
            break;
        case MessageId::DLNA_MSG_WIFI_DISCONNECTED:
            CLOGW("Handler WiFi had disconnected");
            ClearDevice();
            dmcApi_.DestroyEnv();
            isStackInitialized_ = false;
            break;
        default:
            break;
    }
}
} // namespace CastEngineService
} // namespace CastEngine
} // namespace OHOS
